package city.spring.modules.ext;

import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.service.IService;
import reactor.function.Function3;

import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 实体关联关系中间表工具类
 *
 * @author HouKunLin
 * @date 2020/3/26 0026 15:58
 */
public class EntityExtUtils {
    /**
     * 维护关联关系。
     * 例如：文章分类 - 文章 是 多对多关系 ，其中有一个中间表 MidEntity 有两个字段 categoryId 表示文章分类侧（左侧）， articleId 表示文章侧（右侧）。
     * 此时执行 repairRelation(midEntityService, MidEntity::getCategoryId, "文章分类ID值", 分类下新的文章列表 List, fun 创建中间表 MidEntity 的方法)
     * 将会先从中间表中删除 "文章分类ID值" 的所有关联文章信息，然后再给 "文章分类ID值" 添加新的关联关系信息 "分类下新的文章列表 List"，
     * 此方法将不会进行差异对比，而是删除旧的关系信息，然后添加新的关系信息。
     * <p>
     * 左侧为一对多时的“一”侧
     * </p>
     *
     * @param <MT>           关联关系中间表实体对象
     * @param <RT>           关联关系中间表所对应的数据多的一侧的实际对象
     * @param service        操作关联关系中间表的 Service
     * @param relationGetter 关联关系中间表左侧字段方法
     * @param uniqId         关联关系中间表左侧字段值内容
     * @param list           关联关系中间表所对应的数据多的一侧的实际对象列表
     * @param createEntity   创建关联关系中间表的方法
     */
    @Deprecated
    public static <MT, RT> void repairRelation(IService<MT> service, SFunction<MT, ?> relationGetter, Object uniqId, List<RT> list, Function<RT, MT> createEntity) {
        service.lambdaUpdate().eq(relationGetter, uniqId).remove();
        if (list == null) {
            return;
        }
        List<MT> extList = list.stream().map(createEntity).collect(Collectors.toList());
        service.saveBatch(extList);
    }

    /**
     * 维护关联关系。
     * 例如：文章分类 - 文章 是 多对多关系 ，其中有一个中间表 MidEntity 有两个字段 categoryId 表示文章分类侧（左侧）， articleId 表示文章侧（右侧）。
     * 此时执行 repairRelation(midEntityService, MidEntity::getCategoryId, "文章分类ID值", Article::getId, 分类下新的文章列表 List, MidEntity::new)
     * 将会先从中间表中删除 "文章分类ID值" 的所有关联文章信息，然后再给 "文章分类ID值" 添加新的关联关系信息 "分类下新的文章列表 List"，
     * 此方法将不会进行差异对比，而是删除旧的关系信息，然后添加新的关系信息。
     * <p>
     * 左侧为一对多时的“一”侧
     * </p>
     *
     * @param <MT>               关联关系中间表实体对象
     * @param <RT>               关联关系中间表所对应的数据多的一侧的实际对象
     * @param service            操作关联关系中间表的 Service
     * @param relationGetter     关联关系中间表左侧字段方法
     * @param uniqId             关联关系中间表左侧字段值内容
     * @param relationIdFunction 获取List的主键ID方法
     * @param list               关联关系中间表所对应的数据多的一侧的实际对象列表
     * @param createEntity       创建关联关系中间表的方法
     */
    @Deprecated
    public static <MT, RT> void repairRelation(
            IService<MT> service,
            SFunction<MT, ?> relationGetter,
            String uniqId,
            SFunction<RT, String> relationIdFunction,
            List<RT> list,
            Function3<Long, String, String, MT> createEntity) {
        repairRelation(service, relationGetter, uniqId, list,
                (entity) -> createEntity.apply(null, uniqId, relationIdFunction.apply(entity)));
    }

    /**
     * 维护关联关系。
     * 例如：【文章分类Category - 文章Article】 是 多对多关系 ，其中有一个中间表 MidTable 有两个字段 categoryId 表示文章分类侧（左侧）， articleId 表示文章侧（右侧）。
     * 此时执行 repairRelation(service, Category, Category::getId, Category::getArticleList, Article::getId, MidTable::new, MidTable::getCategoryId)
     * 将会先从中间表中删除 "文章分类ID值" 的所有关联文章信息，然后再给 "文章分类ID值" 添加新的关联关系信息 "分类下新的文章列表 List"，
     * 此方法将不会进行差异对比，而是删除旧的关系信息，然后添加新的关系信息。
     * <p>
     * 左侧为一对多时的“一”侧
     * </p>
     *
     * @param <MidTable>          关联关系中间表实体对象
     * @param <Entity>            正在维护的实体对象
     * @param <EntityIdType>      正在维护的实体对象的【主键ID】类型
     * @param <ListEntity>        正在维护的实体对象的【数据列表】类型
     * @param <ListEntityIdType>  正在维护的实体对象的【数据列表】的【主键ID】类型
     * @param service             操作关联关系中间表的 Service
     * @param entity              正在维护的实体对象
     * @param entityGetId         正在维护的实体对象的【获取主键ID】方法
     * @param entityGetList       正在维护的实体对象的【获取数据列表】方法
     * @param entityGetListGetId  正在维护的实体对象的【数据列表】的【获取主键ID】的方法
     * @param createMidTable      创建关联关系中间表的方法：createMidTable.apply 该方法等同于 new MidTable(null, EntityId, ListEntityId)
     * @param midTableGetEntityId 关联关系中间表左侧字段方法（需要清除数据的字段方法）
     * @apiNote 写这段代码的时候，只有我和上帝知道它是干什么的，估计几个月后就只剩下上帝知道了
     * @date 2020-03-26 17:21:05
     */
    public static <MidTable, Entity, EntityIdType, ListEntity, ListEntityIdType> void repairRelation(
            IService<MidTable> service,
            Entity entity,
            SFunction<Entity, EntityIdType> entityGetId,
            SFunction<Entity, List<ListEntity>> entityGetList,
            SFunction<ListEntity, ListEntityIdType> entityGetListGetId,
            Function3<Long, EntityIdType, ListEntityIdType, MidTable> createMidTable,
            SFunction<MidTable, EntityIdType> midTableGetEntityId
    ) {
        EntityIdType entityId = entityGetId.apply(entity);
        List<ListEntity> list = entityGetList.apply(entity);
        service.lambdaUpdate().eq(midTableGetEntityId, entityId).remove();
        if (list == null) {
            return;
        }
        List<MidTable> extList = list.stream()
                .map(listEntity -> createMidTable.apply(null, entityId, entityGetListGetId.apply(listEntity)))
                .collect(Collectors.toList());
        service.saveBatch(extList);
    }
}
